/*
 * Copyright 2011 Clean GWT
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.cleangwt.bootstrap.client.ui;

import java.util.Iterator;
import java.util.NoSuchElementException;

import com.cleangwt.bootstrap.client.style.Panels;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.Widget;

/**
 * A panel that includes a header (top), footer (bottom), and content (middle) area.
 * 
 * @author Jess
 * @version 1.0
 * @since 1.0
 */
public class HeadingPanel extends Panel {

  /**
   * The widget Iterator for this panel.
   * 
   * <p>
   * Widgets are returned in the following order:
   * <ol>
   * <li>Header widget</li>
   * <li>Content widget</li>
   * <li>Footer widget</li>
   * </ol>
   */
  private class WidgetIteratorImpl implements Iterator<Widget> {
    private Widget visitingWidget;

    @Override
    public boolean hasNext() {
      if (visitingWidget == null) {
        return header != null || content != null || footer != null;
      } else if (visitingWidget == header) {
        return content != null || footer != null;
      } else if (visitingWidget == content) {
        return footer != null;
      }
      return false;
    }

    @Override
    public Widget next() {
      if (visitingWidget == null) {
        return visitingWidget = (header != null ? header : (content != null ? content : footer));
      } else if (visitingWidget == header) {
        return visitingWidget = (content != null ? content : footer);
      } else if (visitingWidget == content) {
        return visitingWidget = footer;
      }
      throw new NoSuchElementException();
    }

    @Override
    public void remove() {
      if (visitingWidget == null) {
        throw new IllegalStateException("Widget was already removed.");
      }
      visitingWidget.removeFromParent();
    }

  }

  private Element contentContainer = Document.get().createDivElement();
  private Element footerContainer = Document.get().createDivElement();
  private Element headerContainer = Document.get().createDivElement();

  private Widget header;
  private Widget content;
  private Widget footer;

  /**
   * Creates a heading panel.
   */
  public HeadingPanel() {
    setElement(Document.get().createDivElement());
    this.getElement().appendChild(headerContainer);
    this.getElement().appendChild(contentContainer);
    this.getElement().appendChild(footerContainer);
    headerContainer.setClassName(Panels.HEADING_STYLE);
    contentContainer.setClassName(Panels.BODY_STYLE);
    footerContainer.setClassName(Panels.FOOTER_STYLE);
    headerContainer.getStyle().setDisplay(Display.NONE);
    contentContainer.getStyle().setDisplay(Display.NONE);
    footerContainer.getStyle().setDisplay(Display.NONE);
  }

  /**
   * Creates a heading panel with the given type style.
   * 
   * @param style the base style
   */
  public HeadingPanel(String style) {
    this();
    setStyleBase(style);
  }

  /**
   * Resets the style name with base panel style and the given type style.
   * 
   * @param style the base style
   */
  public void setStyleBase(String style) {
    assert style != null : "Stye must not be null.";
    setStyleName(Panels.PANEL_STYLE + " " + style);
  }

  /**
   * Sets the caption for header.
   * 
   * @param caption the caption for header
   */
  public void setCaption(String caption) {
    HTML header = new HTML(caption);
    header.setStyleName(Panels.TITLE_STYLE);
    setHeaderWidget(header);
  }

  /**
   * /** Adds a widget to this panel.
   * 
   * @param w the child widget to be added
   */
  @Override
  public void add(Widget w) {
    // Add widgets in the order that they appear.
    if (header == null) {
      setHeaderWidget(w);
    } else if (content == null) {
      setContentWidget(w);
    } else if (footer == null) {
      setFooterWidget(w);
    } else {
      throw new IllegalStateException(
          "HeadingPanel already contains header, content, and footer widgets.");
    }
  }

  @Override
  public Iterator<Widget> iterator() {
    return new WidgetIteratorImpl();
  }

  @Override
  public boolean remove(Widget w) {
    // Validate.
    if (w.getParent() != this) {
      return false;
    }
    // Orphan.
    try {
      orphan(w);
    } finally {
      // Physical detach.
      w.getElement().removeFromParent();

      // Logical detach.
      if (w == content) {
        content = null;
        contentContainer.getStyle().setDisplay(Display.NONE);
      } else if (w == header) {
        header = null;
        headerContainer.getStyle().setDisplay(Display.NONE);
      } else if (w == footer) {
        footer = null;
        footerContainer.getStyle().setDisplay(Display.NONE);
      }
    }
    return true;
  }

  /**
   * Set the widget in the content portion between the header and footer.
   * 
   * @param w the widget to use as the content
   */
  public void setContentWidget(Widget w) {
    add(w, content, contentContainer);
    if (w != null) {
      contentContainer.getStyle().clearDisplay();
    }
    // Logical attach.
    content = w;
  }

  /**
   * Set the widget in the header portion at the top of the panel.
   * 
   * @param w the widget to use as the header
   */
  public void setHeaderWidget(Widget w) {
    add(w, header, headerContainer);
    if (w != null) {
      headerContainer.getStyle().clearDisplay();
    }
    // Logical attach.
    header = w;
  }

  /**
   * Set the widget in the header portion at the top of the panel.
   * 
   * @param w the widget to use as the header
   */
  public void setFooterWidget(Widget w) {
    add(w, footer, footerContainer);
    if (w != null) {
      footerContainer.getStyle().clearDisplay();
    }
    // Logical attach.
    footer = w;
  }

  /**
   * Add a widget to the panel in the specified container. Note that this method does not do the
   * logical attach.
   * 
   * @param w the widget to add
   * @param toReplace the widget to replace
   * @param container the container in which to place the widget
   */
  private void add(Widget w, Widget toReplace, Element container) {
    // Validate.
    if (w == toReplace) {
      return;
    }

    // Detach new child.
    if (w != null) {
      w.removeFromParent();
    }

    // Remove old child.
    if (toReplace != null) {
      remove(toReplace);
    }

    if (w != null) {
      // Physical attach.
      container.appendChild(w.getElement());

      adopt(w);
    }
  }

}
